/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { Arg } from './arg';
import { Inst } from './inst';
import { InstDateModel } from './inst';
import { StackSlot } from './stack_solt';
import { FP, GP, PTR } from './symbols';

const CUSTOM_NUM_3: number = 3;
const CUSTOM_NUM_2: number = 2;
const CUSTOM_NUM_8: number = 8;

export class ShuffleCustom {
  static forEachArg(inst: Inst, fun: (arg: Arg, role: string, type: string, width: number) => Arg | null): void {
    let limit = Math.floor(inst._args.length / CUSTOM_NUM_3) * CUSTOM_NUM_3;
    let i = 0;
    while (i < limit) {
      let src = inst._args[i];
      let dst = inst._args[i + 1];
      let widthArg = inst._args[i + CUSTOM_NUM_2];
      let width = widthArg._width!;
      let type = src.isGP() && dst.isGP() ? GP : FP;
      inst.visitArg(i, fun, new InstDateModel(Arg.use, type, width));
      inst.visitArg(i + 1, fun, new InstDateModel(Arg.def, type, width));
      inst.visitArg(i + CUSTOM_NUM_2, fun, new InstDateModel(Arg.use, GP, CUSTOM_NUM_8));
      i += CUSTOM_NUM_3;
    }
  }

  hasNonArgNonControlEffects(inst: Inst): boolean {
    return false;
  }
}

export class PatchCustom {
  static forEachArg(inst: Inst, fun: (arg: Arg, role: string, type: string, width: number) => Arg | null): void {
    for (let i = 0; i < inst._args.length; ++i) {
      let argData = inst.patchArgData[i];
      inst.visitArg(i, fun, argData);
    }
  }

  hasNonArgNonControlEffects(inst: Inst): boolean {
    return true;
  }
}

export class CCallCustom {
  static forEachArg(inst: Inst, fun: (arg: Arg, role: string, type: string, width: number) => Arg | null): void {
    let index = 0;
    index += 1;
    inst.visitArg(index, fun, new InstDateModel(Arg.use, GP, PTR));
  }

  hasNonArgNonControlEffects(inst: Inst): boolean {
    return true;
  }
}

export class ColdCCallCustom {
  static forEachArg(inst: Inst, fun: (arg: Arg, role: string, type: string, width: number) => Arg | null): void {
    CCallCustom.forEachArg(inst, (arg, role, type, width) => {
      return fun(arg, arg.cooled(role), type, width);
    });
  }

  hasNonArgNonControlEffects(inst: Inst): boolean {
    return true;
  }
}
